Finish your scene planner — print the final shot list and count shots per platform
Day 10 of 40
scene_planner.py with a formatted output sectionif not scene: to handle the empty-list edge case.get(key, default) to safely accumulate counts in a dictionary.items() to loop over key-value pairsOpen scene_planner.py from yesterday. You're adding new code after the while True loop — the output section that runs once after the user types "done".
The full addition goes at the bottom of the file:
# --- Print the final scene plan ---
print(f"\n{'=' * 40}")
print(f" SCENE {scene_num} — SHOT LIST")
print(f"{'=' * 40}\n")
if not scene:
print(" No shots added.")
else:
for shot in scene:
print(f" {shot['number']}. [{shot['platform']}] ({shot['type']})")
print(f" {shot['description']}\n")
# Count shots per platform
platform_counts = {}
for shot in scene:
p = shot["platform"]
platform_counts[p] = platform_counts.get(p, 0) + 1
print(f" Total: {len(scene)} shots")
for p, c in platform_counts.items():
print(f" {p}: {c} shot{'s' if c != 1 else ''}")
while loop and runs after the loop ends.
print(f"\n{'=' * 40}")
print(f" SCENE {scene_num} — SHOT LIST")
print(f"{'=' * 40}\n")
'=' * 40 repeats the = character 40 times. In Python, the * operator works on strings too — it means "repeat." "ha" * 3 gives you "hahaha".
Inside an f-string, you can use curly braces {} to run any Python expression — not just variable names. f"{'=' * 40}" evaluates the multiplication inside the braces and inserts the result.
This is a common pattern for building divider lines in terminal output without hardcoding 40 equal signs.
if not scene:
print(" No shots added.")
else:
# ... output code ...
if not scene: checks if the list is empty. An empty list [] is falsy in Python — it evaluates to False in a boolean context. So not scene is True when the list is empty.
This is the Pythonic shorthand for if len(scene) == 0:. Both work, but if not scene: is what experienced Python developers write. You saw this same pattern in yesterday's Day 7 experiments.
It's important to handle this case: if the user immediately types "done" without adding any shots, the loop ends and scene is empty. Without this check, the summary would either show nothing or crash.
for shot in scene:
print(f" {shot['number']}. [{shot['platform']}] ({shot['type']})")
print(f" {shot['description']}\n")
Two print statements per shot — one for the metadata line (number, platform, type), one for the description on the next line, followed by a blank line for readability.
Notice the single quotes around dictionary keys inside the f-string: {shot['number']}. When an f-string uses double quotes on the outside, dictionary key strings must use single quotes inside. This avoids the quote conflict.
platform_counts = {}
for shot in scene:
p = shot["platform"]
platform_counts[p] = platform_counts.get(p, 0) + 1
platform_counts = {} starts with an empty dictionary. The keys will be platform names ("Kling", "Runway", "Veo"); the values will be counts.
platform_counts.get(p, 0) — this is the key trick. .get(key, default) looks up a key in the dictionary. If the key exists, it returns the current value. If the key doesn't exist yet, it returns the default value (0 here) instead of crashing with a KeyError.
So the first time we see "Kling", .get("Kling", 0) returns 0 (doesn't exist yet), and we store 0 + 1 = 1. The second time we see "Kling", .get("Kling", 0) returns 1 (already stored), and we store 1 + 1 = 2. This pattern — counts[key] = counts.get(key, 0) + 1 — is how you count occurrences in Python.
Normal dictionary access crashes if the key doesn't exist:
d = {}
d["Kling"] # KeyError: 'Kling'
d.get("Kling") # None (safe — no crash)
d.get("Kling", 0) # 0 (safe, with a fallback default)
You'll use .get(key, default) constantly — any time you want a safe lookup without knowing if the key exists. It's one of the most practical dictionary methods.
print(f" Total: {len(scene)} shots")
for p, c in platform_counts.items():
print(f" {p}: {c} shot{'s' if c != 1 else ''}")
platform_counts.items() returns each key-value pair as a tuple (key, value). By writing for p, c in, we unpack each tuple into two variables: p for the platform name and c for the count. This is called tuple unpacking.
{'s' if c != 1 else ''} is a ternary expression inside an f-string — it adds an "s" to "shot" when the count is not 1, giving you "2 shots" but "1 shot". This is optional polish, but it matters for output quality.
$ python scene_planner.py
What scene number is this? 2
=== Planning Scene 2 ===
Add shots one at a time. Type 'done' when finished.
Shot description (or 'done'): Wide establishing shot, sunrise over fairway
Platform (Kling / Runway / Veo): Kling
Shot types: wide, medium, close-up, aerial, tracking, slow-mo
Shot type: wide
✓ Shot 1 added.
Shot description (or 'done'): Aerial drone reveal, pulling back
Platform (Kling / Runway / Veo): Veo
Shot types: wide, medium, close-up, aerial, tracking, slow-mo
Shot type: aerial
✓ Shot 2 added.
Shot description (or 'done'): Close-up on ball dropping into cup
Platform (Kling / Runway / Veo): Kling
Shot types: wide, medium, close-up, aerial, tracking, slow-mo
Shot type: close-up
✓ Shot 3 added.
Shot description (or 'done'): done
========================================
SCENE 2 — SHOT LIST
========================================
1. [Kling] (wide)
Wide establishing shot, sunrise over fairway
2. [Veo] (aerial)
Aerial drone reveal, pulling back
3. [Kling] (close-up)
Close-up on ball dropping into cup
Total: 3 shots
Kling: 2 shots
Veo: 1 shot
Cover the right column and try to define each term from memory. Then reveal and check yourself.
| Term | What It Means |
|---|---|
if / elif / else |
Conditional branches — runs different code depending on which condition is true |
| comparison operator | ==, !=, >, <, >=, <= — compares two values and returns True or False |
| logical operator | and, or, not — combines or negates boolean conditions |
for loop |
Iterates over each item in a sequence (list, string, etc.) a fixed number of times |
while loop |
Runs as long as a condition is true — or forever with while True: |
break |
Immediately exits the current loop, regardless of the loop condition |
| list | An ordered, mutable collection of items in [] — items accessed by index |
| index | The position of an item in a list — starts at 0; negative indexes count from the end |
.append() |
Adds an item to the end of a list — the primary way to grow a list |
| dictionary (preview) | A collection of key:value pairs in {} — items accessed by name, not position |
.lower() |
Returns a lowercase copy of a string — used to make comparisons case-insensitive |
count += 1 |
Shorthand for count = count + 1 — increments a variable in place |
if/elif/else does without looking it upfor loop that iterates over a list and does something with each itemwhile True: ... break loop that exits on a specific input.append(), and read items from it by indexThat's completely fine. Dictionaries were a preview in Week 2 — they're the main topic of Week 3. You used them in both build projects this week to build intuition. Next week you'll learn them properly: how to create, modify, and iterate over them, and how to save them to files so your data persists.
scene_planner.py runs with input collection and formatted output.get(key, default) and why it's safer than direct key access